home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkArgv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-08-07  |  11.2 KB  |  433 lines

  1. /*
  2.  * tkArgv.c --
  3.  *
  4.  *    This file contains a procedure that handles table-based
  5.  *    argv-argc parsing.
  6.  *
  7.  * Copyright 1990 Regents of the University of California
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkArgv.c,v 1.12 92/08/07 08:39:48 ouster Exp $ SPRITE (Berkeley)";
  19. #endif
  20.  
  21. #include "tkConfig.h"
  22. #include "tk.h"
  23.  
  24. /*
  25.  * Default table of argument descriptors.  These are normally available
  26.  * in every application.
  27.  */
  28.  
  29. static Tk_ArgvInfo defaultTable[] = {
  30.     {"-help",    TK_ARGV_HELP,    (char *) NULL,    (char *) NULL,
  31.     "Print summary of command-line options and abort"},
  32.     {NULL,    TK_ARGV_END,    (char *) NULL,    (char *) NULL,
  33.     (char *) NULL}
  34. };
  35.  
  36. /*
  37.  * Forward declarations for procedures defined in this file:
  38.  */
  39.  
  40. static void    PrintUsage _ANSI_ARGS_((Tcl_Interp *interp,
  41.             Tk_ArgvInfo *argTable, int flags));
  42.  
  43. /*
  44.  *----------------------------------------------------------------------
  45.  *
  46.  * Tk_ParseArgv --
  47.  *
  48.  *    Process an argv array according to a table of expected
  49.  *    command-line options.  See the manual page for more details.
  50.  *
  51.  * Results:
  52.  *    The return value is a standard Tcl return value.  If an
  53.  *    error occurs then an error message is left in interp->result.
  54.  *    Under normal conditions, both *argcPtr and *argv are modified
  55.  *    to return the arguments that couldn't be processed here (they
  56.  *    didn't match the option table, or followed an TK_ARGV_REST
  57.  *    argument).
  58.  *
  59.  * Side effects:
  60.  *    Variables may be modified, resources may be entered for tkwin,
  61.  *    or procedures may be called.  It all depends on the arguments
  62.  *    and their entries in argTable.  See the user documentation
  63.  *    for details.
  64.  *
  65.  *----------------------------------------------------------------------
  66.  */
  67.  
  68. int
  69. Tk_ParseArgv(interp, tkwin, argcPtr, argv, argTable, flags)
  70.     Tcl_Interp *interp;        /* Place to store error message. */
  71.     Tk_Window tkwin;        /* Window to use for setting Tk options.
  72.                  * NULL means ignore Tk option specs. */
  73.     int *argcPtr;        /* Number of arguments in argv.  Modified
  74.                  * to hold # args left in argv at end. */
  75.     char **argv;        /* Array of arguments.  Modified to hold
  76.                  * those that couldn't be processed here. */
  77.     Tk_ArgvInfo *argTable;    /* Array of option descriptions */
  78.     int flags;            /* Or'ed combination of various flag bits,
  79.                  * such as TK_ARGV_NO_DEFAULTS. */
  80. {
  81.     register Tk_ArgvInfo *infoPtr;
  82.                 /* Pointer to the current entry in the
  83.                  * table of argument descriptions. */
  84.     Tk_ArgvInfo *matchPtr;    /* Descriptor that matches current argument. */
  85.     char *curArg;        /* Current argument */
  86.     register char c;        /* Second character of current arg (used for
  87.                  * quick check for matching;  use 2nd char.
  88.                  * because first char. will almost always
  89.                  * be '-'). */
  90.     int srcIndex;        /* Location from which to read next argument
  91.                  * from argv. */
  92.     int dstIndex;        /* Index into argv to which next unused
  93.                  * argument should be copied (never greater
  94.                  * than srcIndex). */
  95.     int argc;            /* # arguments in argv still to process. */
  96.     int length;            /* Number of characters in current argument. */
  97.     int i;
  98.  
  99.     if (flags & TK_ARGV_DONT_SKIP_FIRST_ARG) {
  100.     srcIndex = dstIndex = 0;
  101.     argc = *argcPtr;
  102.     } else {
  103.     srcIndex = dstIndex = 1;
  104.     argc = *argcPtr-1;
  105.     }
  106.  
  107.     while (argc > 0) {
  108.     curArg = argv[srcIndex];
  109.     srcIndex++;
  110.     argc--;
  111.     c = curArg[1];
  112.     length = strlen(curArg);
  113.  
  114.     /*
  115.      * Loop throught the argument descriptors searching for one with
  116.      * the matching key string.  If found, leave a pointer to it in
  117.      * matchPtr.
  118.      */
  119.  
  120.     matchPtr = NULL;
  121.     for (i = 0; i < 2; i++) {
  122.         if (i == 0) {
  123.         infoPtr = argTable;
  124.         } else {
  125.         infoPtr = defaultTable;
  126.         }
  127.         for (; infoPtr->type != TK_ARGV_END; infoPtr++) {
  128.          if (infoPtr->key == NULL) {
  129.              continue;
  130.          }
  131.          if ((infoPtr->key[1] != c)
  132.              || (strncmp(infoPtr->key, curArg, length) != 0)) {
  133.              continue;
  134.          }
  135.          if ((tkwin == NULL)
  136.              && ((infoPtr->type == TK_ARGV_CONST_OPTION)
  137.              || (infoPtr->type == TK_ARGV_OPTION_VALUE)
  138.              || (infoPtr->type == TK_ARGV_OPTION_NAME_VALUE))) {
  139.              continue;
  140.          }
  141.          if (infoPtr->key[length] == 0) {
  142.              matchPtr = infoPtr;
  143.              goto gotMatch;
  144.          }
  145.          if (flags & TK_ARGV_NO_ABBREV) {
  146.              continue;
  147.          }
  148.          if (matchPtr != NULL) {
  149.              Tcl_AppendResult(interp, "ambiguous option \"", curArg,
  150.                  "\"", (char *) NULL);
  151.              return TCL_ERROR;
  152.          }
  153.          matchPtr = infoPtr;
  154.         }
  155.     }
  156.     if (matchPtr == NULL) {
  157.  
  158.         /*
  159.          * Unrecognized argument.  Just copy it down, unless the caller
  160.          * prefers an error to be registered.
  161.          */
  162.  
  163.         if (flags & TK_ARGV_NO_LEFTOVERS) {
  164.         Tcl_AppendResult(interp, "unrecognized argument \"",
  165.             curArg, "\"", (char *) NULL);
  166.         return TCL_ERROR;
  167.         }
  168.         argv[dstIndex] = curArg;
  169.         dstIndex++;
  170.         continue;
  171.     }
  172.  
  173.     /*
  174.      * Take the appropriate action based on the option type
  175.      */
  176.  
  177.     gotMatch:
  178.     infoPtr = matchPtr;
  179.     switch (infoPtr->type) {
  180.         case TK_ARGV_CONSTANT:
  181.         *((int *) infoPtr->dst) = (int) infoPtr->src;
  182.         break;
  183.         case TK_ARGV_INT:
  184.         if (argc == 0) {
  185.             goto missingArg;
  186.         } else {
  187.             char *endPtr;
  188.  
  189.             *((int *) infoPtr->dst) =
  190.                 strtol(argv[srcIndex], &endPtr, 0);
  191.             if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) {
  192.             Tcl_AppendResult(interp, "expected integer argument ",
  193.                 "for \"", infoPtr->key, "\" but got \"",
  194.                 argv[srcIndex], "\"", (char *) NULL);
  195.             return TCL_ERROR;
  196.             }
  197.             srcIndex++;
  198.             argc--;
  199.         }
  200.         break;
  201.         case TK_ARGV_STRING:
  202.         if (argc == 0) {
  203.             goto missingArg;
  204.         } else {
  205.             *((char **)infoPtr->dst) = argv[srcIndex];
  206.             srcIndex++;
  207.             argc--;
  208.         }
  209.         break;
  210.         case TK_ARGV_UID:
  211.         if (argc == 0) {
  212.             goto missingArg;
  213.         } else {
  214.             *((Tk_Uid *)infoPtr->dst) = Tk_GetUid(argv[srcIndex]);
  215.             srcIndex++;
  216.             argc--;
  217.         }
  218.         break;
  219.         case TK_ARGV_REST:
  220.         *((int *) infoPtr->dst) = dstIndex;
  221.         goto argsDone;
  222.         case TK_ARGV_FLOAT:
  223.         if (argc == 0) {
  224.             goto missingArg;
  225.         } else {
  226.             char *endPtr;
  227.  
  228.             *((double *) infoPtr->dst) =
  229.                 strtod(argv[srcIndex], &endPtr);
  230.             if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) {
  231.             Tcl_AppendResult(interp, "expected floating-point ",
  232.                 "argument for \"", infoPtr->key,
  233.                 "\" but got \"", argv[srcIndex], "\"",
  234.                 (char *) NULL);
  235.             return TCL_ERROR;
  236.             }
  237.             srcIndex++;
  238.             argc--;
  239.         }
  240.         break;
  241.         case TK_ARGV_FUNC: {
  242.         int (*handlerProc)();
  243.  
  244.         handlerProc = (int (*)())infoPtr->src;
  245.         
  246.         if ((*handlerProc)(infoPtr->dst, infoPtr->key,
  247.             argv[srcIndex])) {
  248.             srcIndex += 1;
  249.             argc -= 1;
  250.         }
  251.         break;
  252.         }
  253.         case TK_ARGV_GENFUNC: {
  254.         int        (*handlerProc)();
  255.  
  256.         handlerProc = (int (*)())infoPtr->src;
  257.  
  258.         argc = (*handlerProc)(infoPtr->dst, interp, infoPtr->key,
  259.             argc, argv+srcIndex);
  260.         if (argc < 0) {
  261.             return TCL_ERROR;
  262.         }
  263.         break;
  264.         }
  265.         case TK_ARGV_HELP:
  266.         PrintUsage (interp, argTable, flags);
  267.         return TCL_ERROR;
  268.         case TK_ARGV_CONST_OPTION:
  269.         Tk_AddOption(tkwin, infoPtr->dst, infoPtr->src,
  270.             TK_INTERACTIVE_PRIO);
  271.         break;
  272.         case TK_ARGV_OPTION_VALUE:
  273.         if (argc < 1) {
  274.             goto missingArg;
  275.         }
  276.         Tk_AddOption(tkwin, infoPtr->dst, argv[srcIndex],
  277.             TK_INTERACTIVE_PRIO);
  278.         srcIndex++;
  279.         argc--;
  280.         break;
  281.         case TK_ARGV_OPTION_NAME_VALUE:
  282.         if (argc < 2) {
  283.             Tcl_AppendResult(interp, "\"", curArg,
  284.                 "\" option requires two following arguments",
  285.                 (char *) NULL);
  286.             return TCL_ERROR;
  287.         }
  288.         Tk_AddOption(tkwin, argv[srcIndex], argv[srcIndex+1],
  289.             TK_INTERACTIVE_PRIO);
  290.         srcIndex += 2;
  291.         argc -= 2;
  292.         break;
  293.         default:
  294.         sprintf(interp->result, "bad argument type %d in Tk_ArgvInfo",
  295.             infoPtr->type);
  296.         return TCL_ERROR;
  297.     }
  298.     }
  299.  
  300.     /*
  301.      * If we broke out of the loop because of an OPT_REST argument,
  302.      * copy the remaining arguments down.
  303.      */
  304.  
  305.     argsDone:
  306.     while (argc) {
  307.     argv[dstIndex] = argv[srcIndex];
  308.     srcIndex++;
  309.     dstIndex++;
  310.     argc--;
  311.     }
  312.     argv[dstIndex] = (char *) NULL;
  313.     *argcPtr = dstIndex;
  314.     return TCL_OK;
  315.  
  316.     missingArg:
  317.     Tcl_AppendResult(interp, "\"", curArg,
  318.         "\" option requires an additional argument", (char *) NULL);
  319.     return TCL_ERROR;
  320. }
  321.  
  322. /*
  323.  *----------------------------------------------------------------------
  324.  *
  325.  * PrintUsage --
  326.  *
  327.  *    Generate a help string describing command-line options.
  328.  *
  329.  * Results:
  330.  *    Interp->result will be modified to hold a help string
  331.  *    describing all the options in argTable, plus all those
  332.  *    in the default table unless TK_ARGV_NO_DEFAULTS is
  333.  *    specified in flags.
  334.  *
  335.  * Side effects:
  336.  *    None.
  337.  *
  338.  *----------------------------------------------------------------------
  339.  */
  340.  
  341. static void
  342. PrintUsage(interp, argTable, flags)
  343.     Tcl_Interp *interp;        /* Place information in this interp's
  344.                  * result area. */
  345.     Tk_ArgvInfo *argTable;    /* Array of command-specific argument
  346.                  * descriptions. */
  347.     int flags;            /* If the TK_ARGV_NO_DEFAULTS bit is set
  348.                  * in this word, then don't generate
  349.                  * information for default options. */
  350. {
  351.     register Tk_ArgvInfo *infoPtr;
  352.     int width, i, numSpaces;
  353. #define NUM_SPACES 20
  354.     static char spaces[] = "                    ";
  355.     char tmp[30];
  356.  
  357.     /*
  358.      * First, compute the width of the widest option key, so that we
  359.      * can make everything line up.
  360.      */
  361.  
  362.     width = 4;
  363.     for (i = 0; i < 2; i++) {
  364.     for (infoPtr = i ? defaultTable : argTable;
  365.         infoPtr->type != TK_ARGV_END; infoPtr++) {
  366.         int length;
  367.         if (infoPtr->key == NULL) {
  368.         continue;
  369.         }
  370.         length = strlen(infoPtr->key);
  371.         if (length > width) {
  372.         width = length;
  373.         }
  374.     }
  375.     }
  376.  
  377.     Tcl_AppendResult(interp, "Command-specific options:", (char *) NULL);
  378.     for (i = 0; ; i++) {
  379.     for (infoPtr = i ? defaultTable : argTable;
  380.         infoPtr->type != TK_ARGV_END; infoPtr++) {
  381.         if ((infoPtr->type == TK_ARGV_HELP) && (infoPtr->key == NULL)) {
  382.         Tcl_AppendResult(interp, "\n", infoPtr->help, (char *) NULL);
  383.         continue;
  384.         }
  385.         Tcl_AppendResult(interp, "\n ", infoPtr->key, ":", (char *) NULL);
  386.         numSpaces = width + 1 - strlen(infoPtr->key);
  387.         while (numSpaces > 0) {
  388.         if (numSpaces >= NUM_SPACES) {
  389.             Tcl_AppendResult(interp, spaces, (char *) NULL);
  390.         } else {
  391.             Tcl_AppendResult(interp, spaces+NUM_SPACES-numSpaces,
  392.                 (char *) NULL);
  393.         }
  394.         numSpaces -= NUM_SPACES;
  395.         }
  396.         Tcl_AppendResult(interp, infoPtr->help, (char *) NULL);
  397.         switch (infoPtr->type) {
  398.         case TK_ARGV_INT: {
  399.             sprintf(tmp, "%d", *((int *) infoPtr->dst));
  400.             Tcl_AppendResult(interp, "\n\t\tDefault value: ",
  401.                 tmp, (char *) NULL);
  402.             break;
  403.         }
  404.         case TK_ARGV_FLOAT: {
  405.             sprintf(tmp, "%lg", *((double *) infoPtr->dst));
  406.             Tcl_AppendResult(interp, "\n\t\tDefault value: ",
  407.                 tmp, (char *) NULL);
  408.             break;
  409.         }
  410.         case TK_ARGV_STRING: {
  411.             char *string;
  412.  
  413.             string = *((char **) infoPtr->dst);
  414.             if (string != NULL) {
  415.             Tcl_AppendResult(interp, "\n\t\tDefault value: \"",
  416.                 string, "\"", (char *) NULL);
  417.             }
  418.             break;
  419.         }
  420.         default: {
  421.             break;
  422.         }
  423.         }
  424.     }
  425.  
  426.     if ((flags & TK_ARGV_NO_DEFAULTS) || (i > 0)) {
  427.         break;
  428.     }
  429.     Tcl_AppendResult(interp, "\nGeneric options for all commands:",
  430.         (char *) NULL);
  431.     }
  432. }
  433.